use crate::db::db_student;
use crate::db::entities::student::Model;
use crate::rest_api::api_student::{CreateParams, SearchParams, Student, UpdateParams};

use anyhow::Result;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::str2id;
use rbase_iam::db::define::SYSTEM_ID;
use rbase_iam::rest_api::api_user::UserToken;

pub async fn find_student_by_id(user_token: &UserToken, student_id: &String) -> Result<Student, ApiError> {
    let student_model = db_student::find_by_id_and_owner_ids(str2id(student_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(student_id.to_string()))?;

    Ok(student_from_model(&student_model).await?)
}

pub async fn student_from_model(student_model: &Model) -> Result<Student> {
    let student = Student::from_model(student_model);
    Ok(student)
}

pub async fn delete_student_by_id(user_token: &UserToken, student_id: &String) -> Result<(), ApiError> {
    let student_model = db_student::find_by_id_and_owner_ids(str2id(student_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(student_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && student_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_student::delete_by_id(student_model.id).await?;
    Ok(())
}

pub async fn update_student_by_id(user_token: &UserToken, student_id: &String, new_student: &UpdateParams) -> Result<(), ApiError> {
    let old_model = db_student::find_by_id_and_owner_ids(str2id(student_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(student_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: Some(new_student.name.clone()),
        code: Some(new_student.code.clone()),
        ..Default::default()
    };
    db_student::update(new_model).await?;
    Ok(())
}

pub async fn create_student(user_token: &UserToken, new_student: &CreateParams) -> Result<Student> {
    let new_model = Model {
        name: Some(new_student.name.clone()),
        code: Some(new_student.code.clone()),
        owner_id: Some(user_token.id),
        ..Default::default()
    };
    let created_model = db_student::insert(new_model).await?;
    Ok(student_from_model(&created_model).await?)
}

pub async fn search(user_token: &UserToken, student_search_params: &SearchParams) -> Result<(Vec<Student>, i64)> {
    let (students, total) = db_student::search(&db_student::SearchParams {
        name: student_search_params.name.clone(),
        offset: student_search_params.offset,
        limit: student_search_params.limit,
        order_by: student_search_params.order_by.clone(),
        order: student_search_params.order.clone(),
        owner_ids: user_token.owner_ids.clone(),
    })
    .await?;

    let mut student_results = Vec::new();
    for student in students {
        student_results.push(student_from_model(&student).await?);
    }

    Ok((student_results, total))
}
